/*
* FS.java
* Copyright (C) 2003
* 
* $Id: FS.java,v 1.15 2005/11/13 13:36:00 cawe Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using cvar_t = Suake2.UI.game.cvar_t;
using Sys = Suake2.UI.sys.Sys;
//UPGRADE_TODO: The type 'java.nio.ByteBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteBuffer = java.nio.ByteBuffer;
//UPGRADE_TODO: The type 'java.nio.ByteOrder' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteOrder = java.nio.ByteOrder;
//UPGRADE_TODO: The type 'java.nio.channels.FileChannel' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using FileChannel = java.nio.channels.FileChannel;
namespace Suake2.UI.qcommon
{
	
	/// <summary> FS
	/// 
	/// </summary>
	/// <author>  cwei
	/// </author>
	public sealed class FS:Globals
	{
		private class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				jake2.qcommon.FS.Path_f();
			}
		}
		private class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				jake2.qcommon.FS.Link_f();
			}
		}
		private class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				jake2.qcommon.FS.Dir_f();
			}
		}
		
		/*
		* ==================================================
		* 
		* QUAKE FILESYSTEM
		* 
		* ==================================================
		*/
		
		public class packfile_t
		{
			internal const int SIZE = 64;
			
			internal const int NAME_SIZE = 56;
			
			internal System.String name; // char name[56]
			
			internal int filepos, filelen;
			
			public override System.String ToString()
			{
				return name + " [ length: " + filelen + " pos: " + filepos + " ]";
			}
		}
		
		public class pack_t
		{
			internal System.String filename;
			
			//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
			internal System.IO.FileStream handle;
			
			internal ByteBuffer backbuffer;
			
			internal int numfiles;
			
			internal System.Collections.Hashtable files; // with packfile_t entries
		}
		
		public static System.String fs_gamedir;
		
		private static System.String fs_userdir;
		
		public static cvar_t fs_basedir;
		
		public static cvar_t fs_cddir;
		
		public static cvar_t fs_gamedirvar;
		
		public class filelink_t
		{
			internal System.String from;
			
			internal int fromlength;
			
			internal System.String to;
		}
		
		// with filelink_t entries
		//UPGRADE_TODO: Class 'java.util.LinkedList' was converted to 'System.Collections.ArrayList' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilLinkedList'"
		public static System.Collections.IList fs_links = new System.Collections.ArrayList();
		
		public class searchpath_t
		{
			internal System.String filename;
			
			internal pack_t pack; // only one of filename or pack will be used
			
			internal searchpath_t next;
		}
		
		public static searchpath_t fs_searchpaths;
		
		// without gamedirs
		public static searchpath_t fs_base_searchpaths;
		
		/*
		* All of Quake's data access is through a hierchal file system, but the
		* contents of the file system can be transparently merged from several
		* sources.
		* 
		* The "base directory" is the path to the directory holding the quake.exe
		* and all game directories. The sys_* files pass this to host_init in
		* quakeparms_t->basedir. This can be overridden with the "-basedir" command
		* line parm to allow code debugging in a different directory. The base
		* directory is only used during filesystem initialization.
		* 
		* The "game directory" is the first tree on the search path and directory
		* that all generated files (savegames, screenshots, demos, config files)
		* will be saved to. This can be overridden with the "-game" command line
		* parameter. The game directory can never be changed while quake is
		* executing. This is a precacution against having a malicious server
		* instruct clients to write files over areas they shouldn't.
		*  
		*/
		
		/*
		* CreatePath
		* 
		* Creates any directories needed to store the given filename.
		*/
		public static void  CreatePath(System.String path)
		{
			int index = path.LastIndexOf('/');
			// -1 if not found and 0 means write to root
			if (index > 0)
			{
				System.IO.FileInfo f = new System.IO.FileInfo(path.Substring(0, (index) - (0)));
				//UPGRADE_TODO: Method 'java.io.File.mkdirs' was converted to 'System.IO.Directory.CreateDirectory' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioFilemkdirs'"
				if (!System.IO.Directory.CreateDirectory(f.FullName) && !System.IO.Directory.Exists(f.FullName))
				{
					Com.Printf("can't create path \"" + path + '"' + "\n");
				}
			}
		}
		
		/*
		* FCloseFile
		* 
		* For some reason, other dll's can't just call fclose() on files returned
		* by FS_FOpenFile...
		*/
		//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
		public static void  FCloseFile(System.IO.FileStream file)
		{
			file.Close();
		}
		
		public static void  FCloseFile(System.IO.Stream stream)
		{
			stream.Close();
		}
		
		public static int FileLength(System.String filename)
		{
			searchpath_t search;
			System.String netpath;
			pack_t pak;
			int i;
			filelink_t link;
			
			file_from_pak = 0;
			
			// check for links first
			//UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
			for (System.Collections.IEnumerator it = fs_links.GetEnumerator(); it.MoveNext(); )
			{
				//UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
				link = (filelink_t) it.Current;
				
				if (String.Compare(filename, 0, link.from, 0, link.fromlength) == 0)
				{
					netpath = link.to + filename.Substring(link.fromlength);
					System.IO.FileInfo file = new System.IO.FileInfo(netpath);
					//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
					if (file.canRead())
					{
						Com.DPrintf("link file: " + netpath + '\n');
						return (int) SupportClass.FileLength(file);
					}
					return - 1;
				}
			}
			
			// search through the path, one element at a time
			
			for (search = fs_searchpaths; search != null; search = search.next)
			{
				// is the element a pak file?
				if (search.pack != null)
				{
					// look through all the pak file elements
					pak = search.pack;
					filename = filename.ToLower();
					packfile_t entry = (packfile_t) pak.files[filename];
					
					if (entry != null)
					{
						// found it!
						file_from_pak = 1;
						Com.DPrintf("PackFile: " + pak.filename + " : " + filename + '\n');
						// open a new file on the pakfile
						System.IO.FileInfo file = new System.IO.FileInfo(pak.filename);
						//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
						if (!file.canRead())
						{
							Com.Error(Defines.ERR_FATAL, "Couldn't reopen " + pak.filename);
						}
						return entry.filelen;
					}
				}
				else
				{
					// check a file in the directory tree
					netpath = search.filename + '/' + filename;
					
					System.IO.FileInfo file = new System.IO.FileInfo(netpath);
					//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
					if (!file.canRead())
						continue;
					
					Com.DPrintf("FindFile: " + netpath + '\n');
					
					return (int) SupportClass.FileLength(file);
				}
			}
			Com.DPrintf("FindFile: can't find " + filename + '\n');
			return - 1;
		}
		
		public static int file_from_pak = 0;
		
		/*
		* FOpenFile
		* 
		* Finds the file in the search path. returns a RadomAccesFile. Used for
		* streaming data out of either a pak file or a seperate file.
		*/
		//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
		public static System.IO.FileStream FOpenFile(System.String filename)
		{
			searchpath_t search;
			System.String netpath;
			pack_t pak;
			int i;
			filelink_t link;
			System.IO.FileInfo file = null;
			
			file_from_pak = 0;
			
			// check for links first
			//UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
			for (System.Collections.IEnumerator it = fs_links.GetEnumerator(); it.MoveNext(); )
			{
				//UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
				link = (filelink_t) it.Current;
				
				//			if (!strncmp (filename, link->from, link->fromlength))
				if (String.Compare(filename, 0, link.from, 0, link.fromlength) == 0)
				{
					netpath = link.to + filename.Substring(link.fromlength);
					file = new System.IO.FileInfo(netpath);
					//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
					if (file.canRead())
					{
						//Com.DPrintf ("link file: " + netpath +'\n');
						return SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(file, "r");
					}
					return null;
				}
			}
			
			//
			// search through the path, one element at a time
			//
			for (search = fs_searchpaths; search != null; search = search.next)
			{
				// is the element a pak file?
				if (search.pack != null)
				{
					// look through all the pak file elements
					pak = search.pack;
					filename = filename.ToLower();
					packfile_t entry = (packfile_t) pak.files[filename];
					
					if (entry != null)
					{
						// found it!
						file_from_pak = 1;
						//Com.DPrintf ("PackFile: " + pak.filename + " : " +
						// filename + '\n');
						file = new System.IO.FileInfo(pak.filename);
						//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
						if (!file.canRead())
							Com.Error(Defines.ERR_FATAL, "Couldn't reopen " + pak.filename);
						//UPGRADE_ISSUE: Method 'java.io.FileDescriptor.valid' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFileDescriptor'"
						//UPGRADE_ISSUE: Method 'java.io.RandomAccessFile.getFD' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioRandomAccessFilegetFD'"
						if (pak.handle == null || !pak.handle.getFD().valid())
						{
							// hold the pakfile handle open
							pak.handle = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(pak.filename, "r");
						}
						// open a new file on the pakfile
						
						//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
						System.IO.FileStream raf = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(file, "r");
						raf.Seek(entry.filepos, System.IO.SeekOrigin.Begin);
						
						return raf;
					}
				}
				else
				{
					// check a file in the directory tree
					netpath = search.filename + '/' + filename;
					
					file = new System.IO.FileInfo(netpath);
					//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
					if (!file.canRead())
						continue;
					
					//Com.DPrintf("FindFile: " + netpath +'\n');
					
					return SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(file, "r");
				}
			}
			//Com.DPrintf ("FindFile: can't find " + filename + '\n');
			return null;
		}
		
		// read in blocks of 64k
		public const int MAX_READ = 0x10000;
		
		/// <summary> Read
		/// 
		/// Properly handles partial reads
		/// </summary>
		//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
		public static void  Read(sbyte[] buffer, int len, System.IO.FileStream f)
		{
			
			int block, remaining;
			int offset = 0;
			int read = 0;
			bool tries = true;
			
			// read in chunks for progress bar
			remaining = len;
			
			while (remaining != 0)
			{
				block = System.Math.Min(remaining, MAX_READ);
				try
				{
					read = SupportClass.ReadInput(f, buffer, offset, block);
				}
				catch (System.IO.IOException e)
				{
					//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
					Com.Error(Defines.ERR_FATAL, e.ToString());
				}
				
				if (read == 0)
				{
					Com.Error(Defines.ERR_FATAL, "FS_Read: 0 bytes read");
				}
				else if (read == - 1)
				{
					Com.Error(Defines.ERR_FATAL, "FS_Read: -1 bytes read");
				}
				//
				// do some progress bar thing here...
				//
				remaining -= read;
				offset += read;
			}
		}
		
		/*
		* LoadFile
		* 
		* Filename are reletive to the quake search path a null buffer will just
		* return the file content as byte[]
		*/
		public static sbyte[] LoadFile(System.String path)
		{
			//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
			System.IO.FileStream file;
			
			sbyte[] buf = null;
			int len = 0;
			
			// TODO hack for bad strings (fuck \0)
			int index = path.IndexOf((System.Char) '\x0000');
			if (index != - 1)
				path = path.Substring(0, (index) - (0));
			
			// look for it in the filesystem or pack files
			len = FileLength(path);
			
			if (len < 1)
				return null;
			
			try
			{
				file = FOpenFile(path);
				//Read(buf = new byte[len], len, h);
				buf = new sbyte[len];
				SupportClass.ReadInput(file, buf, 0, buf.Length);
				file.Close();
			}
			catch (System.IO.IOException e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Error(Defines.ERR_FATAL, e.ToString());
			}
			return buf;
		}
		
		/*
		* LoadMappedFile
		* 
		* Filename are reletive to the quake search path a null buffer will just
		* return the file content as ByteBuffer (memory mapped)
		*/
		public static ByteBuffer LoadMappedFile(System.String filename)
		{
			searchpath_t search;
			System.String netpath;
			pack_t pak;
			int i;
			filelink_t link;
			System.IO.FileInfo file = null;
			
			int fileLength = 0;
			FileChannel channel = null;
			System.IO.FileStream input = null;
			ByteBuffer buffer = null;
			
			file_from_pak = 0;
			
			try
			{
				// check for links first
				//UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
				for (System.Collections.IEnumerator it = fs_links.GetEnumerator(); it.MoveNext(); )
				{
					//UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
					link = (filelink_t) it.Current;
					
					if (String.Compare(filename, 0, link.from, 0, link.fromlength) == 0)
					{
						netpath = link.to + filename.Substring(link.fromlength);
						file = new System.IO.FileInfo(netpath);
						//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
						if (file.canRead())
						{
							//UPGRADE_TODO: Constructor 'java.io.FileInputStream.FileInputStream' was converted to 'System.IO.FileStream.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioFileInputStreamFileInputStream_javaioFile'"
							input = new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
							channel = input.getChannel();
							fileLength = (int) channel.size();
							buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength);
							input.Close();
							return buffer;
						}
						return null;
					}
				}
				
				//
				// search through the path, one element at a time
				//
				for (search = fs_searchpaths; search != null; search = search.next)
				{
					// is the element a pak file?
					if (search.pack != null)
					{
						// look through all the pak file elements
						pak = search.pack;
						filename = filename.ToLower();
						packfile_t entry = (packfile_t) pak.files[filename];
						
						if (entry != null)
						{
							// found it!
							file_from_pak = 1;
							//Com.DPrintf ("PackFile: " + pak.filename + " : " +
							// filename + '\n');
							file = new System.IO.FileInfo(pak.filename);
							//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
							if (!file.canRead())
								Com.Error(Defines.ERR_FATAL, "Couldn't reopen " + pak.filename);
							//UPGRADE_ISSUE: Method 'java.io.FileDescriptor.valid' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFileDescriptor'"
							//UPGRADE_ISSUE: Method 'java.io.RandomAccessFile.getFD' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioRandomAccessFilegetFD'"
							if (pak.handle == null || !pak.handle.getFD().valid())
							{
								// hold the pakfile handle open
								pak.handle = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(pak.filename, "r");
							}
							// open a new file on the pakfile
							if (pak.backbuffer == null)
							{
								channel = pak.handle.getChannel();
								pak.backbuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, pak.handle.Length);
								channel.close();
							}
							pak.backbuffer.position(entry.filepos);
							buffer = pak.backbuffer.slice();
							buffer.limit(entry.filelen);
							return buffer;
						}
					}
					else
					{
						// check a file in the directory tree
						netpath = search.filename + '/' + filename;
						
						file = new System.IO.FileInfo(netpath);
						//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
						if (!file.canRead())
							continue;
						
						//Com.DPrintf("FindFile: " + netpath +'\n');
						//UPGRADE_TODO: Constructor 'java.io.FileInputStream.FileInputStream' was converted to 'System.IO.FileStream.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioFileInputStreamFileInputStream_javaioFile'"
						input = new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
						channel = input.getChannel();
						fileLength = (int) channel.size();
						buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength);
						input.Close();
						return buffer;
					}
				}
			}
			catch (System.Exception e)
			{
			}
			try
			{
				if (input != null)
					input.Close();
				else if (channel != null && channel.isOpen())
					channel.close();
			}
			catch (System.IO.IOException ioe)
			{
			}
			return null;
		}
		
		/*
		* FreeFile
		*/
		public static void  FreeFile(sbyte[] buffer)
		{
			buffer = null;
		}
		
		internal const int IDPAKHEADER = (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P');
		
		internal class dpackheader_t
		{
			internal int ident; // IDPAKHEADER
			
			internal int dirofs;
			
			internal int dirlen;
		}
		
		internal const int MAX_FILES_IN_PACK = 4096;
		
		// buffer for C-Strings char[56]
		internal static sbyte[] tmpText = new sbyte[packfile_t.NAME_SIZE];
		
		/*
		* LoadPackFile
		* 
		* Takes an explicit (not game tree related) path to a pak file.
		* 
		* Loads the header and directory, adding the files at the beginning of the
		* list so they override previous pack files.
		*/
		internal static pack_t LoadPackFile(System.String packfile)
		{
			
			dpackheader_t header;
			System.Collections.Hashtable newfiles;
			//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
			System.IO.FileStream file;
			int numpackfiles = 0;
			pack_t pack = null;
			//		unsigned checksum;
			//
			try
			{
				file = SupportClass.RandomAccessFileSupport.CreateRandomAccessFile(packfile, "r");
				FileChannel fc = file.getChannel();
				ByteBuffer packhandle = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.Length);
				packhandle.order(ByteOrder.LITTLE_ENDIAN);
				
				fc.close();
				
				if (packhandle == null || packhandle.limit() < 1)
					return null;
				//
				header = new dpackheader_t();
				header.ident = packhandle.getInt();
				header.dirofs = packhandle.getInt();
				header.dirlen = packhandle.getInt();
				
				if (header.ident != IDPAKHEADER)
					Com.Error(Defines.ERR_FATAL, packfile + " is not a packfile");
				
				numpackfiles = header.dirlen / packfile_t.SIZE;
				
				if (numpackfiles > MAX_FILES_IN_PACK)
					Com.Error(Defines.ERR_FATAL, packfile + " has " + numpackfiles + " files");
				
				newfiles = System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable(numpackfiles));
				
				packhandle.position(header.dirofs);
				
				// parse the directory
				packfile_t entry = null;
				
				for (int i = 0; i < numpackfiles; i++)
				{
					packhandle.get_Renamed(tmpText);
					
					entry = new packfile_t();
					entry.name = new System.String(SupportClass.ToCharArray(SupportClass.ToByteArray(tmpText))).Trim();
					entry.filepos = packhandle.getInt();
					entry.filelen = packhandle.getInt();
					
					newfiles[entry.name.ToLower()] = entry;
				}
			}
			catch (System.IO.IOException e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.DPrintf(e.Message + '\n');
				return null;
			}
			
			pack = new pack_t();
			pack.filename = new System.Text.StringBuilder(packfile).ToString();
			pack.handle = file;
			pack.numfiles = numpackfiles;
			pack.files = newfiles;
			
			Com.Printf("Added packfile " + packfile + " (" + numpackfiles + " files)\n");
			
			return pack;
		}
		
		/*
		* AddGameDirectory
		* 
		* Sets fs_gamedir, adds the directory to the head of the path, then loads
		* and adds pak1.pak pak2.pak ...
		*/
		internal static void  AddGameDirectory(System.String dir)
		{
			int i;
			searchpath_t search;
			pack_t pak;
			System.String pakfile;
			
			fs_gamedir = new System.Text.StringBuilder(dir).ToString();
			
			//
			// add the directory to the search path
			// ensure fs_userdir is first in searchpath
			search = new searchpath_t();
			search.filename = new System.Text.StringBuilder(dir).ToString();
			if (fs_searchpaths != null)
			{
				search.next = fs_searchpaths.next;
				fs_searchpaths.next = search;
			}
			else
			{
				fs_searchpaths = search;
			}
			
			//
			// add any pak files in the format pak0.pak pak1.pak, ...
			//
			for (i = 0; i < 10; i++)
			{
				pakfile = dir + "/pak" + i + ".pak";
				//UPGRADE_ISSUE: Method 'java.io.File.canRead' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javaioFilecanRead'"
				if (!(new System.IO.FileInfo(pakfile).canRead()))
					continue;
				
				pak = LoadPackFile(pakfile);
				if (pak == null)
					continue;
				
				search = new searchpath_t();
				search.pack = pak;
				search.filename = "";
				search.next = fs_searchpaths;
				fs_searchpaths = search;
			}
		}
		
		/*
		* Gamedir
		* 
		* Called to find where to write a file (demos, savegames, etc)
		* this is modified to <user.home>/.jake2 
		*/
		public static System.String Gamedir()
		{
			return (fs_userdir != null)?fs_userdir:Globals.BASEDIRNAME;
		}
		
		/*
		* BaseGamedir
		* 
		* Called to find where to write a downloaded file
		*/
		public static System.String BaseGamedir()
		{
			return (fs_gamedir != null)?fs_gamedir:Globals.BASEDIRNAME;
		}
		
		/*
		* ExecAutoexec
		*/
		public static void  ExecAutoexec()
		{
			System.String dir = fs_userdir;
			
			System.String name;
			if (dir != null && dir.Length > 0)
			{
				name = dir + "/autoexec.cfg";
			}
			else
			{
				name = fs_basedir.string_Renamed + '/' + Globals.BASEDIRNAME + "/autoexec.cfg";
			}
			
			int canthave = Defines.SFF_SUBDIR | Defines.SFF_HIDDEN | Defines.SFF_SYSTEM;
			
			if (Sys.FindAll(name, 0, canthave) != null)
			{
				Cbuf.AddText("exec autoexec.cfg\n");
			}
		}
		
		/*
		* SetGamedir
		* 
		* Sets the gamedir and path to a different directory.
		*/
		public static void  SetGamedir(System.String dir)
		{
			searchpath_t next;
			
			if (dir.IndexOf("..") != - 1 || dir.IndexOf("/") != - 1 || dir.IndexOf("\\") != - 1 || dir.IndexOf(":") != - 1)
			{
				Com.Printf("Gamedir should be a single filename, not a path\n");
				return ;
			}
			
			//
			// free up any current game dir info
			//
			while (fs_searchpaths != fs_base_searchpaths)
			{
				if (fs_searchpaths.pack != null)
				{
					try
					{
						fs_searchpaths.pack.handle.Close();
					}
					catch (System.IO.IOException e)
					{
						//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.getMessage' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
						Com.DPrintf(e.Message + '\n');
					}
					// clear the hashtable
					fs_searchpaths.pack.files.Clear();
					fs_searchpaths.pack.files = null;
					fs_searchpaths.pack = null;
				}
				next = fs_searchpaths.next;
				fs_searchpaths = null;
				fs_searchpaths = next;
			}
			
			//
			// flush all data, so it will be forced to reload
			//
			if ((Globals.dedicated != null) && (Globals.dedicated.value_Renamed == 0.0f))
				Cbuf.AddText("vid_restart\nsnd_restart\n");
			
			fs_gamedir = fs_basedir.string_Renamed + '/' + dir;
			
			if (dir.Equals(Globals.BASEDIRNAME) || (dir.Length == 0))
			{
				Cvar.FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET);
				Cvar.FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO);
			}
			else
			{
				Cvar.FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET);
				if (fs_cddir.string_Renamed != null && fs_cddir.string_Renamed.Length > 0)
					AddGameDirectory(fs_cddir.string_Renamed + '/' + dir);
				
				AddGameDirectory(fs_basedir.string_Renamed + '/' + dir);
			}
		}
		
		/*
		* Link_f
		* 
		* Creates a filelink_t
		*/
		public static void  Link_f()
		{
			filelink_t entry = null;
			
			if (Cmd.Argc() != 3)
			{
				Com.Printf("USAGE: link <from> <to>\n");
				return ;
			}
			
			// see if the link already exists
			//UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
			for (System.Collections.IEnumerator it = fs_links.GetEnumerator(); it.MoveNext(); )
			{
				//UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
				entry = (filelink_t) it.Current;
				
				if (entry.from.Equals(Cmd.Argv(1)))
				{
					if (Cmd.Argv(2).Length < 1)
					{
						// delete it
						//UPGRADE_ISSUE: Method 'java.util.Iterator.remove' was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1000_javautilIteratorremove'"
						it.remove();
						return ;
					}
					entry.to = new System.Text.StringBuilder(Cmd.Argv(2)).ToString();
					return ;
				}
			}
			
			// create a new link if the <to> is not empty
			if (Cmd.Argv(2).Length > 0)
			{
				entry = new filelink_t();
				entry.from = new System.Text.StringBuilder(Cmd.Argv(1)).ToString();
				entry.fromlength = entry.from.Length;
				entry.to = new System.Text.StringBuilder(Cmd.Argv(2)).ToString();
				fs_links.Add(entry);
			}
		}
		
		/*
		* ListFiles
		*/
		public static System.String[] ListFiles(System.String findname, int musthave, int canthave)
		{
			System.String[] list = null;
			
			System.IO.FileInfo[] files = Sys.FindAll(findname, musthave, canthave);
			
			if (files != null)
			{
				list = new System.String[files.Length];
				for (int i = 0; i < files.Length; i++)
				{
					list[i] = files[i].FullName;
				}
			}
			
			return list;
		}
		
		/*
		* Dir_f
		*/
		public static void  Dir_f()
		{
			System.String path = null;
			System.String findname = null;
			System.String wildcard = "*.*";
			System.String[] dirnames;
			
			if (Cmd.Argc() != 1)
			{
				wildcard = Cmd.Argv(1);
			}
			
			while ((path = NextPath(path)) != null)
			{
				System.String tmp = findname;
				
				findname = path + '/' + wildcard;
				
				if (tmp != null)
					tmp.replaceAll("\\\\", "/");
				
				Com.Printf("Directory of " + findname + '\n');
				Com.Printf("----\n");
				
				dirnames = ListFiles(findname, 0, 0);
				
				if (dirnames != null)
				{
					int index = 0;
					for (int i = 0; i < dirnames.Length; i++)
					{
						if ((index = dirnames[i].LastIndexOf('/')) > 0)
						{
							Com.Printf(dirnames[i].Substring(index + 1, (dirnames[i].Length) - (index + 1)) + '\n');
						}
						else
						{
							Com.Printf(dirnames[i] + '\n');
						}
					}
				}
				
				Com.Printf("\n");
			}
		}
		
		/*
		* Path_f
		*/
		public static void  Path_f()
		{
			
			searchpath_t s;
			filelink_t link;
			
			Com.Printf("Current search path:\n");
			for (s = fs_searchpaths; s != null; s = s.next)
			{
				if (s == fs_base_searchpaths)
					Com.Printf("----------\n");
				if (s.pack != null)
					Com.Printf(s.pack.filename + " (" + s.pack.numfiles + " files)\n");
				else
					Com.Printf(s.filename + '\n');
			}
			
			Com.Printf("\nLinks:\n");
			//UPGRADE_TODO: Method 'java.util.Iterator.hasNext' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratorhasNext'"
			for (System.Collections.IEnumerator it = fs_links.GetEnumerator(); it.MoveNext(); )
			{
				//UPGRADE_TODO: Method 'java.util.Iterator.next' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilIteratornext'"
				link = (filelink_t) it.Current;
				Com.Printf(link.from + " : " + link.to + '\n');
			}
		}
		
		/*
		* NextPath
		* 
		* Allows enumerating all of the directories in the search path
		*/
		public static System.String NextPath(System.String prevpath)
		{
			searchpath_t s;
			System.String prev;
			
			if (prevpath == null || prevpath.Length == 0)
				return fs_gamedir;
			
			prev = fs_gamedir;
			for (s = fs_searchpaths; s != null; s = s.next)
			{
				if (s.pack != null)
					continue;
				
				if ((System.Object) prevpath == (System.Object) prev)
					return s.filename;
				
				prev = s.filename;
			}
			
			return null;
		}
		
		/*
		* InitFilesystem
		*/
		public static void  InitFilesystem()
		{
			Cmd.AddCommand("path", new AnonymousClassxcommand_t());
			Cmd.AddCommand("link", new AnonymousClassxcommand_t1());
			Cmd.AddCommand("dir", new AnonymousClassxcommand_t2());
			
			//UPGRADE_TODO: Method 'java.lang.System.getProperty' was converted to 'System.Environment.GetEnvironmentVariable' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangSystemgetProperty_javalangString'"
			fs_userdir = System.Environment.GetEnvironmentVariable("userprofile") + "/.jake2";
			FS.CreatePath(fs_userdir + "/");
			FS.AddGameDirectory(fs_userdir);
			
			//
			// basedir <path>
			// allows the game to run from outside the data tree
			//
			fs_basedir = Cvar.Get("basedir", ".", CVAR_NOSET);
			
			//
			// cddir <path>
			// Logically concatenates the cddir after the basedir for
			// allows the game to run from outside the data tree
			//
			
			setCDDir();
			
			//
			// start up with baseq2 by default
			//
			AddGameDirectory(fs_basedir.string_Renamed + '/' + Globals.BASEDIRNAME);
			
			// any set gamedirs will be freed up to here
			markBaseSearchPaths();
			
			// check for game override
			fs_gamedirvar = Cvar.Get("game", "", CVAR_LATCH | CVAR_SERVERINFO);
			
			if (fs_gamedirvar.string_Renamed.Length > 0)
				SetGamedir(fs_gamedirvar.string_Renamed);
		}
		
		/// <summary> set baseq2 directory</summary>
		internal static void  setCDDir()
		{
			fs_cddir = Cvar.Get("cddir", "", CVAR_ARCHIVE);
			if (fs_cddir.string_Renamed.Length > 0)
				AddGameDirectory(fs_cddir.string_Renamed);
		}
		
		internal static void  markBaseSearchPaths()
		{
			// any set gamedirs will be freed up to here
			fs_base_searchpaths = fs_searchpaths;
		}
		
		//	RAFAEL
		/*
		* Developer_searchpath
		*/
		public static int Developer_searchpath(int who)
		{
			
			int ch;
			// PMM - warning removal
			//	 char *start;
			searchpath_t s;
			
			if (who == 1)
			// xatrix
				ch = 'x';
			else if (who == 2)
				ch = 'r';
			
			for (s = fs_searchpaths; s != null; s = s.next)
			{
				if (s.filename.IndexOf("xatrix") != - 1)
					return 1;
				
				if (s.filename.IndexOf("rogue") != - 1)
					return 2;
			}
			
			return 0;
		}
	}
}